package com.gitee.l0km.codegen.base;

import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.gitee.l0km.codegen.base.Method.Parameter;
import com.gitee.l0km.com4j.base.Assert;
import com.gitee.l0km.com4j.basex.TypeNameUtils;
import com.gitee.l0km.com4j.basex.TypeNameUtils.FullName;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;

public abstract class AbstractSchema {
	protected final Map<String, Class<?>> importedList = new HashMap<String,Class<?>>();
	protected Map<String, Class<?>> importedListBackup ;
	protected final FullNameByImporList fullNameInstanceByImportList;
	protected Class<? > baseClass=null;
	protected AbstractSchema() {
		this.fullNameInstanceByImportList = new FullNameByImporList(importedList);
	}
	protected void initImportList() {
	}
	public Map<String, Class<?>> getImportedList() {
		return importedList;
	}
	/**
	 * @since 2.3.3
	 */
	public boolean imported(Type type) {
		return importedList.containsValue(type);
	}
	public void removeClassFromImports(Class<?> clazz){
		if(null != clazz){
			importedList.remove(clazz.getSimpleName());
		}
	}
	public void removeClassFromImports(Class<?>... array){
		if(null !=array){
			removeClassFromImports(Arrays.asList(array));
		}
	}
	public void removeClassFromImports(Collection<Class<?>> collection){
		if(null != collection){
			for(Class<?> clazz:collection){
				removeClassFromImports(clazz);
			}
		}
	}
	private static final String getTypeName0(Type type, FullName fn) {
		Assert.notNull(type,"type");
		return TypeNameUtils.getTypeName(type,fn);
	}
	public final String getTypeDefine(Type type) {
		return getTypeName0(type,fullNameInstanceByImportList);
	}

	public final String getTypeName(Type type) {
		return getTypeName0(type,fullNameInstanceByImportList);
	}
	public final String getFullTypeName(Type type) {
		Assert.notNull(type,"type");
		return TypeNameUtils.getTypeName(type,true);
	}
	public final String getRawTypeName(Type type) {
		if(type instanceof ParameterizedType){
			ParameterizedType parameterizedType = (ParameterizedType)type;
			return getTypeName(parameterizedType.getRawType());
		}
		return getTypeName(type);
	}
	
	/**
	 * 将类型变量转为定义字符串,如 {@code T extends Serializable &  Comparable<T> }
	 * @param input
	 * @param fullName 是否输出类的全名
	 */
	private final String defineTypeVariable(TypeVariable<?> input,boolean fullName){
		StringBuffer buffer = new StringBuffer(input.getName());
		Type[] bounds = input.getBounds();
		if(!Object.class.equals(bounds[0])){
			buffer.append(" extends ");
			boolean first=true;
			for(Type type:bounds){
				if(!first){
					buffer.append(" & ");
				}
				first=true;
				if(fullName){
					buffer.append(getFullTypeName(type));
				}else{
					buffer.append(getTypeName(type));
				}
			}
		}
		return buffer.toString();
	
	}
	/**
	 * 将类或方法(实现{@link GenericDeclaration}的类)中的类型变量转为定义字符串,
	 * 如 {@code <T extends Serializable &  Comparable<T>,S> }
	 * @param genericDeclaration
	 * @param fullName 是否输出类的全名
	 * @return  {@code genericDeclaration}为{@code null}或无类型变量时返回空字符串
	 */
	public final String defineTypeVariables(GenericDeclaration genericDeclaration,final boolean fullName){
		if(null != genericDeclaration){
			TypeVariable<?>[] typeparms = genericDeclaration.getTypeParameters();	
			if(typeparms.length > 0){
				List<String> typeDefines = Lists.transform(Arrays.asList(typeparms), new Function<TypeVariable<?>,String>(){

					@Override
					public String apply(TypeVariable<?> input) {
						return defineTypeVariable(input,fullName);
					}});
				return " <" + Joiner.on(",").join(typeDefines) + "> ";
			}
		}
		return "";
	}
	/**
	 * @see #defineTypeVariables(GenericDeclaration, boolean)
	 */
	public final String defineTypeVariables(Method method,boolean fullName){
		return null == method ? "" : defineTypeVariables(method.delegate(),fullName);
	}

	/**
	 * 将方法中的参数列表转为定义字符串
	 * @param params
	 * @param isVarArgs 最后的参数是否为变长参数
	 * @param fullName 是否输出类的全名
	 * @return  {@code genericDeclaration}为{@code null}或无类型变量时返回空字符串
	 */
	private final List<String> paramsDefine(Type[] params, boolean isVarArgs,boolean fullName){
		List<String> types = Lists.newLinkedList();
		if(null != params){
            for (int j = 0; j < params.length; j++) {
                String param = fullName ? getFullTypeName(params[j]) : getTypeName(params[j]);
                if (isVarArgs && (j == params.length - 1)) {
                    param = param.replaceFirst("\\[\\]$", "...");// replace T[] with T...
                }
                types.add(param);
            }
		}
		return types;
	}
	/**
	 * 将方法中的参数类型列表转为定义字符串
	 * @param method
	 * @param fullName 是否输出类的全名
	 * @return  {@code genericDeclaration}为{@code null}或无类型变量时返回空字符串
	 */
	private final List<String> paramTypeNames(java.lang.reflect.Member input, boolean fullName){
		if(input instanceof java.lang.reflect.Method){
			java.lang.reflect.Method member = (java.lang.reflect.Method)input;
			return paramsDefine(member.getGenericParameterTypes(),member.isVarArgs(),fullName);
		}
		if(input instanceof java.lang.reflect.Constructor<?>){
			java.lang.reflect.Constructor<?> member = (java.lang.reflect.Constructor<?>)input;
			return paramsDefine(member.getGenericParameterTypes(),member.isVarArgs(),fullName);
		}
		return Collections.emptyList();
	}
	/**
	 * 将方法中的参数类型列表转为参数定义字符串(参数类型 参数名),支持变长参数正确输出
	 * @param method
	 * @param fullName 是否输出类的全名
	 * @return  {@code genericDeclaration}为{@code null}或无类型变量时返回空字符串
	 */
	public final String paramsDefine(Method method, boolean fullName){
		if(null == method){
			return "";
		}
		List<String> types = paramTypeNames(method.delegate(),fullName);
		Parameter[] names = method.getParameters();
		StringBuilder sb = new StringBuilder();
		for(int i=0; i<names.length; ++ i){
			if(i > 0){
				sb.append(",");
			}
			sb.append(types.get(i) + " "  + names[i].name);
		}
		return sb.toString();
	}
	
	public final void addImportedClass(Type... types) {
		CodeGenUtils.addImportedTypes(importedList, types);
	}
	public final void addImportedClass(Iterable<Type> types) {
		CodeGenUtils.addImportedTypes(importedList, types);
	}
	protected final void addImportedClass(String... types) throws ClassNotFoundException {
		CodeGenUtils.addImportedTypes(importedList, types);
	}

	/**
	 * {@link #importedList}中的类名排序输出
	 */
	public final String[] getImportedClassNames(){		
		return getImportedClassNames(null).toArray( new String[0]);		
	}
	/**
	 * {@link #importedList}中的类名排序输出
	 * @param omitPkg 忽略的包名，属于该package下的类不输出
	 */
	public final List<String> getImportedClassNames(String omitPkg){
		return getImportedClassNames(omitPkg,Predicates.alwaysTrue());
	}
	/**
	 * {@link #importedList}中的类名排序输出
	 * @param omitPkg 忽略的包名，属于该package下的类不输出
	 */
	private final List<String> getImportedClassNames(String omitPkg,Predicate<Class<?>>filter){
		return FluentIterable.from(CodeGenUtils.sortClass(this.importedList.values()))
			.filter(filter)
			.filter(clazz->!clazz.getPackage().getName().equals(omitPkg))
			.transform(clazz->clazz.getName().replace('$', '.')).toList();
	}
	public Class<?> getBaseClass() {
		return baseClass;
	}
	public abstract boolean compile();

	protected void addImportedClassFromMethod(Method method, boolean importExceptions, boolean importReturn, boolean importAnnotation) {
		addImportedClass(method.getGenericParameterTypes());
		if(importExceptions) {
			addImportedClass(method.getGenericExceptionTypes());
		}
		if(importReturn) {
			addImportedClass(method.getGenericReturnType());
		}
	}

	protected final void addImportedClassFromMethods(Collection<Method> methodsNeedGenerated, 
			boolean importExceptions, boolean importReturn, boolean importAnnotation) {
		Iterator<Method> it = methodsNeedGenerated.iterator();
		while (it.hasNext()) {
			Method im = it.next();
			addImportedClassFromMethod(im, importExceptions, importReturn, importAnnotation);
		}
	}
	/**
	 * 备份{@link #importedList},必须与{@link #restoreImportedList()}配对使用
	 */
	public void backupImportedList(){
		this.importedListBackup = new HashMap<String,Class<?>>(this.importedList);
	}
	/**
	 * 恢复{@link #importedList},必须与{@link #backupImportedList()}配对使用
	 */
	public void restoreImportedList(){
		importedList.clear();
		if(null == importedListBackup){
			throw new NullPointerException("importedListBackup is null,must call backupImportedList firstly");
		}
		importedList.putAll(importedListBackup);
	}

	/**
	 * 根据指定的方法列表重新生成imported list
	 * @param methods
	 */
	public void rebuildImportedList(List<Method> methods){
		rebuildImportedList(methods,true,true, true);
	}
	/**
	 * 根据指定的方法列表重新生成imported list
	 * @param methods
	 * @param importExceptions 是否导入异常类型
	 * @param importReturn 是否导入返回类型
	 * @param importAnnotation 是否导入注解类型
	 * @since 2.3.0
	 */
	public void rebuildImportedList(List<Method> methods,
			boolean importExceptions, boolean importReturn, boolean importAnnotation){
		if(methods != null){
			//compile();
			importedList.clear();
			initImportList();
			addImportedClassFromMethods(methods, importExceptions, importReturn, importAnnotation);
		}
	}
	public String getName() {
		return baseClass.getName();
	}
	public String getPackage(){
		return baseClass.getPackage().getName();
	}
	public void setOutPackage(String pkg) {
		fullNameInstanceByImportList.setPkg(pkg);
	}
}
